/***********************************************************************************
 * 文件名： mcu_adv_timer.h
 * 作者： 刘言
 * 版本： 1
 * 说明：
 * 		高级定时器驱动。PY32F002B的高级定时器是TIM1。
 * 修改记录：
 * 	2024/9/11: 初版。 刘言。
***********************************************************************************/
#ifndef _MCU_ADV_TIMER_H_
#define _MCU_ADV_TIMER_H_

#include "mcu.h"


typedef enum _advtim_stf_t
{
    ADVTIM_STF_UIF = (1 << 0),      // 更新中断标记
    ADVTIM_STF_CC1IF = (1 << 1),    // 捕获/比较 1 中断标记，读TIM1_CCR1 自动清 0
    ADVTIM_STF_CC2IF = (1 << 2),    // 捕获/比较 2 中断标记，读TIM1_CCR2 自动清 0
    ADVTIM_STF_CC3IF = (1 << 3),    // 捕获/比较 3 中断标记，读TIM1_CCR3 自动清 0
    ADVTIM_STF_CC4IF = (1 << 4),    // 捕获/比较 4 中断标记，读TIM1_CCR4 自动清 0
    ADVTIM_STF_COMIF = (1 << 5),    // CcxE、 CcxNE、 OCxM 已被更新标记
    ADVTIM_STF_TIF = (1 << 6),    // 触发器中断标记
    ADVTIM_STF_BIF = (1 << 7),    // 刹车中断标记
    ADVTIM_STF_CC1OF = (1 << 9),    // 捕获1 过捕获标记，上一次捕获后捕获标记未被清除又捕获了
    ADVTIM_STF_CC2OF = (1 << 10),    // 捕获1 过捕获标记，上一次捕获后捕获标记未被清除又捕获了
    ADVTIM_STF_CC3OF = (1 << 11),    // 捕获1 过捕获标记，上一次捕获后捕获标记未被清除又捕获了
    ADVTIM_STF_CC4OF = (1 << 12),    // 捕获1 过捕获标记，上一次捕获后捕获标记未被清除又捕获了
    ADVTIM_STF_IC1IR = (1 << 16),   // 通道1捕获到上升沿标记，读TIM1_CCR1 自动清 0
    ADVTIM_STF_IC2IR = (1 << 17),   // 通道2捕获到上升沿标记，读TIM1_CCR2 自动清 0
    ADVTIM_STF_IC3IR = (1 << 18),   // 通道3捕获到上升沿标记，读TIM1_CCR3 自动清 0
    ADVTIM_STF_IC4IR = (1 << 19),   // 通道4捕获到上升沿标记，读TIM1_CCR4 自动清 0
    ADVTIM_STF_IC1IF = (1 << 20),   // 通道1捕获到下降沿标记，读TIM1_CCR1 自动清 0
    ADVTIM_STF_IC2IF = (1 << 21),   // 通道2捕获到下降沿标记，读TIM1_CCR2 自动清 0
    ADVTIM_STF_IC3IF = (1 << 22),   // 通道3捕获到下降沿标记，读TIM1_CCR3 自动清 0
    ADVTIM_STF_IC4IF = (1 << 23),   // 通道4捕获到下降沿标记，读TIM1_CCR4 自动清 0
    ADVTIM_STF_ALL = (int)0XFFFFFFFF
}advtim_stf_t;

#define ADVTIM_IC_TRIG_RISE 0x1
#define ADVTIM_IC_TRIG_FALL 0x3
#define ADVTIM_IC_TRIG_RISE_FALL 0xB

typedef enum _advtim_ic_trig_t
{
    ADVTIM_IC_TRIG_NONE = 0,
    ADVTIM_IC_TRIG_CH1_RISE = (ADVTIM_IC_TRIG_RISE << 0), // CH1 使能，捕获上升沿
    ADVTIM_IC_TRIG_CH1_FALL = (ADVTIM_IC_TRIG_FALL << 0), // CH1 使能，捕获下降沿
    ADVTIM_IC_TRIG_CH1_RISE_FALL = (ADVTIM_IC_TRIG_RISE_FALL << 0), // CH1 使能，捕获上升沿和下降沿
    ADVTIM_IC_TRIG_CH2_RISE = (ADVTIM_IC_TRIG_RISE << 4), // CH1 使能，捕获上升沿
    ADVTIM_IC_TRIG_CH2_FALL = (ADVTIM_IC_TRIG_FALL << 4), // CH1 使能，捕获下降沿
    ADVTIM_IC_TRIG_CH2_RISE_FALL = (ADVTIM_IC_TRIG_RISE_FALL << 4), // CH1 使能，捕获上升沿和下降沿
    ADVTIM_IC_TRIG_CH3_RISE = (ADVTIM_IC_TRIG_RISE << 8), // CH1 使能，捕获上升沿
    ADVTIM_IC_TRIG_CH3_FALL = (ADVTIM_IC_TRIG_FALL << 8), // CH1 使能，捕获下降沿
    ADVTIM_IC_TRIG_CH3_RISE_FALL = (ADVTIM_IC_TRIG_RISE_FALL << 8), // CH1 使能，捕获上升沿和下降沿
    ADVTIM_IC_TRIG_CH4_RISE = (ADVTIM_IC_TRIG_RISE << 12), // CH1 使能，捕获上升沿
    ADVTIM_IC_TRIG_CH4_FALL = (ADVTIM_IC_TRIG_FALL << 12), // CH1 使能，捕获下降沿
    ADVTIM_IC_TRIG_CH4_RISE_FALL = (ADVTIM_IC_TRIG_RISE_FALL << 12), // CH1 使能，捕获上升沿和下降沿
    _ADVTIM_IC_TRIG_END
}advtim_ic_trig_t;

#define ADVTIM_CH1 0
#define ADVTIM_CH2 1
#define ADVTIM_CH3 2
#define ADVTIM_CH4 3

/**
 * 高级定时器TIM1初始化。未配置NVIC、GPIO。
 */
void AdvTim_Init();

STATIC_FORCE_IN_LINE void AdvTim_Run()
{
    SET_BIT(TIM1->CR1, TIM_CR1_CEN);
}

STATIC_FORCE_IN_LINE void AdvTim_Stop()
{
    CLEAR_BIT(TIM1->CR1, TIM_CR1_CEN);
}

/**
 * 清零计数寄存器
 */
STATIC_FORCE_IN_LINE void AdvTim_ClrCount()
{
    TIM1->CNT = 0;  // 可考虑使用设置UG实现，可同时清零预分频计数器，但会产生更新事件
}

/**
 * 获取状态/中断标志
 * @param status 哪个状态，取值见 advtim_stf_t 。多个状态可用 | 连接，只要其中一个状态有效就会返回true。
 */
STATIC_FORCE_IN_LINE bool AdvTim_GetStatusFlag(advtim_stf_t status)
{
    return ((TIM1->SR & (status)) > 0);
}

/**
 * 清除状态/中断标志
 * @param status 哪个状态，取值见 advtim_stf_t 。多个状态可用 | 连接，一次清除。
 */
STATIC_FORCE_IN_LINE void AdvTim_ClrStatusFlag(advtim_stf_t status)
{
    // TIM1->SR &= ~(status);
    TIM1->SR = ~(status);
}

/**
 * 设置指定通道的比较值，设置后立即生效还是更新事件产生后生效取决于 OCxPE。
 * @param ch 通道号 0~3。
 * @param value 比较值 0~0xffff。
 */
STATIC_FORCE_IN_LINE void AdvTim_SetCompValue(int ch, int value)
{
    *((&TIM1->CCR1) + ch) = value;
}

/**
 * 设置各个通道的捕获事件。将修改 CCxE CCxP CCxNE CCxNP。
 * 注意: 不可以通过多次调用设置多个事件; 别忘记将对应通道设置为捕获输入模式。
 * @param event 各个通道的捕获事件，取值见 advtim_hw_trig_t，可用 | 链接多个通道设置值。
 */
STATIC_FORCE_IN_LINE void AdvTim_SetCapEvent(advtim_ic_trig_t event)
{
    TIM1->CCER = event;
}

/**
 * 获取指定通道的捕获值。
 * @param ch 通道号 0~3。
 */
STATIC_FORCE_IN_LINE u32 AdvTim_GetCapValue(int ch)
{
    return *((&TIM1->CCR1) + ch);
}

#endif
